/* rotates an IBM format picture */
/* and bsaves it                 */


#include <stdio.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <dos.h>
#include <bios.h>
#include <malloc.h>
#include <string.h>    /* declares movedata function */

/* screen modes */
#define TEXT     3
#define MED_RES  5
#define HI_RES   6

/* default adapter */
int ADAPTER=MED_RES;

#define   FRAMEBUFFER 0xb800
int       CGALEAF[2]={0,8192};
#define   CGALINE    80
#define   SCREENSIZE 16385

char far  *screenbuffer;
char far  *reversebuffer;

void STUFF_BUFF(void)
{
    screenbuffer = _fmalloc(SCREENSIZE);
    reversebuffer= _fmalloc(SCREENSIZE);
}


int setcrtmode(unsigned _CRT_MODE)
{
    union REGS rin,rout;

    if ((_bios_equiplist() & 0x30) == 0x30)
        {
        printf("Sorry... Cga Compatible Displays Only...");
         exit(0);
        }

    rin.h.ah = 0;
    rin.h.al = _CRT_MODE;
    int86(0x10,&rin,&rout);
    return 0;

}


unsigned int byteword(unsigned char a, unsigned char b){return b<<8|a;}
unsigned char pcxheader[128];

int checkforpcx(char *name)
{
    FILE *fp; /* reads a ZSOFT .PCX header but ignores the color map */
    int i;    /* we only want CGA compatible full screens.           */

    unsigned int zsoft,version,codetype,pixbits;
    unsigned int xmin, ymin, xmax, ymax;
    unsigned int hres, vres;
    unsigned int no_planes, bytesperline;
    int invalid = -1, valid = 0, status=valid;

    /* read the file header */
    if((fp=fopen(name,"rb"))==NULL)return -1;
    for(i=0;i!=128;i++)pcxheader[i]=fgetc(fp);
    fclose(fp);

    zsoft   =pcxheader[0];
    version =pcxheader[1];
    codetype=pcxheader[2];
    pixbits =pcxheader[3];

    if(zsoft!=10)        status = invalid;
    if(codetype!=1)      status = invalid;
    if(pixbits != 2 )
       if(pixbits != 1)  status = invalid;

    xmin=byteword(pcxheader[4],pcxheader[5]);
    ymin=byteword(pcxheader[6],pcxheader[7]);
    xmax=byteword(pcxheader[8],pcxheader[9]);
    ymax=byteword(pcxheader[10],pcxheader[11]);
    hres=byteword(pcxheader[12],pcxheader[13]);
    vres=byteword(pcxheader[14],pcxheader[15]);
    no_planes   =pcxheader[65];
    bytesperline=byteword(pcxheader[66],pcxheader[67]);
    if(xmin != 0  )      status = invalid;
    if(ymin != 0  )      status = invalid;
    if(xmax != 319)
       if(xmax!=639)     status = invalid;
    if(ymax != 199)      status = invalid;
    if(hres != 320)
       if(hres!=640)     status = invalid;
       if(hres==640)ADAPTER=HI_RES;
       else ADAPTER=MED_RES;
    if(vres != 200)      status = invalid;
    if(no_planes!=1)     status = invalid;
    if(bytesperline !=80)status = invalid;
    /* we can ignore the color map since we        */
    /* are limiting ourselves to CGA modes         */
    /* so we will not handle over 2-bits per pixel */
    return status;

}


/* a microsoft compatible bsaved image format descriptor */
char BSAVED_header[7]={
    '\xfd','\x00','\xb8','\x00','\x00','\x00','\x40'};

int PCXREAD(char *pcxfilename)
{
    unsigned int byteoff=0,packet,width=0;
    FILE *fp;
    unsigned char byte,bytecount;
    long wordcount,target;

    if(checkforpcx(pcxfilename)==-1)return-1;
    fp = fopen(pcxfilename,"rb");

    target = filelength(fileno(fp));
    for(wordcount=0;wordcount!=128;wordcount++)byte=fgetc(fp);

    do{ bytecount=1;                          /* start with a seed count */
        byte=fgetc(fp);
        wordcount++;
                                              /* check to see if its raw */
        if(0xC0 == (0xC0 &byte)){             /* if its not, run encoded */
                    bytecount= 0x3f &byte;
                    byte=fgetc(fp);
                    wordcount++;
                    }
        for(packet=0;packet<bytecount;packet++){
                     screenbuffer[byteoff]=byte;
                     byteoff++;
                     }
        }while(wordcount<target);
        fclose(fp);
        return(0);
}


int B_READ(char *name)
{
    int fh,fh2,y,i,status=0;


    char headbuf[7];
    char linebuffer[CGALINE];
    int offset=0;                              /* window the YREGs */
    int bos;                                   /* bottom of screen */
    long target=16384,target2,header=7;
    /* open the file twice,eliminate the interleaf,read contiguous */

    if((fh = open(name,O_RDONLY|O_BINARY)) == -1)     status = -1;
    if((fh2 = open(name,O_RDONLY|O_BINARY)) == -1)    status = -1;
    if(status==0)read(fh,headbuf,7)                              ;
    for(i=0;i!=4;i++)if(status!=0 || headbuf[i]!=BSAVED_header[i]){
                                                      close(fh)  ;
                                                      close(fh2) ;
                                                      return -1  ;}

    target2=(target/2)+header          ;
    lseek(fh,(long)(header),SEEK_SET)  ;
    lseek(fh2,(long)(target2),SEEK_SET);
    bos=200                            ;/*filesize divide by scanlines*/

          for(y=0;y<bos;y+=2){
                read(fh,linebuffer,CGALINE);
                for(i=0;i!=CGALINE;i++){
                        screenbuffer[offset]=linebuffer[i];offset++;
                        }
                read(fh2,linebuffer,CGALINE);
                for(i=0;i!=CGALINE;i++){
                        screenbuffer[offset]=linebuffer[i];offset++;
                        }
                }
            close(fh)  ;
            close(fh2) ;
            return 0   ;

}

int cread(char *name)
{
 int status;


 if((status=B_READ(name))!=0)
     if((status=PCXREAD(name))==-1)
         return(-1);
         setcrtmode(ADAPTER);
  return 0;
}



int cload()
{
    unsigned int y,inset=0,offset=0;

    for(y=0;y<200;){
    movedata(FP_SEG(reversebuffer), FP_OFF(reversebuffer)+offset,
                   FRAMEBUFFER,(CGALEAF[0]+inset),CGALINE);
                   offset+=CGALINE;
                   y++;
    movedata(FP_SEG(reversebuffer), FP_OFF(reversebuffer)+offset,
                   FRAMEBUFFER,(CGALEAF[1]+inset),CGALINE);
                   offset+=CGALINE;
                   inset+=CGALINE;
                   y++;
                   }

     return(0);

}

main(int argc,char *argv[])
{
   int status;
   char buf1[66],buf2[66];
   char *wordptr;


    switch (argc)
        {
          case 3:                     /* 2 args */
             STUFF_BUFF();

             if((status=cread(argv[1]))==0)
             {
                rotato();
                cload();
                bsave(argv[2]);
                }
             setcrtmode(TEXT);
             break;

        }
    printf("That's all folks!\n");
    exit(0);
}








rotato()
{

   unsigned char bit[4]  ;
   unsigned char mask[4] ;
   int i,x,y,x2;
   unsigned char byte;

   mask[0]=3;
   mask[1]=12;
   mask[2]=48;
   mask[3]=192;

   for(y=0;y<200;y++)
    {
        i=y*80;
        x2 = 0;
        for(x=79;x>-1;x--)
        {
            byte=screenbuffer[i+x]  ;
            bit[0]=byte&mask[0]     ;
            bit[1]=(byte&mask[1])>>2;
            bit[2]=(byte&mask[2])>>4;
            bit[3]=(byte&mask[3])>>6;

            byte=bit[0]<<6|bit[1]<<4|bit[2]<<2|bit[3];
            reversebuffer[i+x2]=byte;
            x2++;
            }
    }

}

bsave(char *name)
{
    FILE *fp;
    char far *crt = MK_FP(0xb800,0x0000);
    int i;
    char temp;

    fp=fopen(name,"wb");
    for(i=0;i!=7;i++)fputc(BSAVED_header[i],fp);
    for(i=0;i<16385;i++)
    {
        temp = *crt++;
        fputc(temp,fp);
    }
    fputc('\x1a',fp);
    fclose(fp);

}

